package com.sneakxy.mybase4j.generator.jdbc;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.sneakxy.mybase4j.generator.config.Jdbc;

/**
 * @author 潜行的青衣
 * @see
 */
public class JdbcHelper {
	
	private static Logger log = LogManager.getLogger(JdbcHelper.class);
	
	private final static Map<Integer, Class<?>> TYPES = new HashMap<Integer, Class<?>>();
	
	private final static Map<Integer, String> TYPE_NAMES = new HashMap<Integer, String>();
	
	static {
		TYPES.put(Types.BIGINT, Long.class);
		TYPES.put(Types.BOOLEAN, Boolean.class);
		TYPES.put(Types.BIT, Boolean.class);
		TYPES.put(Types.CHAR, String.class);
		TYPES.put(Types.VARCHAR, String.class);
		TYPES.put(Types.DOUBLE, Double.class);
		TYPES.put(Types.FLOAT, Float.class);
		TYPES.put(Types.DATE, String.class);
		TYPES.put(Types.DECIMAL, String.class);
		TYPES.put(Types.INTEGER, Integer.class);
		TYPES.put(Types.NUMERIC, BigDecimal.class);
		TYPES.put(Types.TIMESTAMP, String.class);
		TYPES.put(Types.BINARY, byte[].class);
		TYPES.put(Types.TINYINT, Short.class);
		
		TYPE_NAMES.put(Types.BIGINT, "BIGINT");
		TYPE_NAMES.put(Types.BOOLEAN, "BOOLEAN");
		TYPE_NAMES.put(Types.BIT, "BIT");
		TYPE_NAMES.put(Types.CHAR, "CHAR");
		TYPE_NAMES.put(Types.VARCHAR, "VARCHAR");
		TYPE_NAMES.put(Types.DOUBLE, "DOUBLE");
		TYPE_NAMES.put(Types.FLOAT, "FLOAT");
		TYPE_NAMES.put(Types.DATE, "DATE");
		TYPE_NAMES.put(Types.DECIMAL, "DECIMAL");
		TYPE_NAMES.put(Types.INTEGER, "INTEGER");
		TYPE_NAMES.put(Types.NUMERIC, "NUMERIC");
		TYPE_NAMES.put(Types.TIMESTAMP, "TIMESTAMP");
		TYPE_NAMES.put(Types.BINARY, "BINARY");
		TYPE_NAMES.put(Types.TINYINT, "TINYINT");
	}
	
	public interface ResultDealable<T> {
		
		public List<T> onResultSetCallback(ResultSet rs) throws SQLException;
		
	}
	
	private Jdbc jdbc;
	
	private Connection conn;

	public JdbcHelper(Jdbc jdbc) {
		this.jdbc = jdbc;
	}
	
	public static Map<Integer, Class<?>> getSqlTypeConverter() {
		return TYPES;
	}
	
	public static Class<?> getJavaType(Integer type) {
		Class<?> result = TYPES.get(type);
		return result == null ? String.class : result;
	}
	
	public static String getSqlTypeName(Integer type) {
		String result = TYPE_NAMES.get(type);
		return result == null ? "" : result;
	}
	
	public void openConnection() throws SQLException, ClassNotFoundException {
		this.openConnection(true);
	}
	
	public void openConnection(boolean autoCommit) throws SQLException, ClassNotFoundException {
		try {
			Class.forName(jdbc.getDriver());
			this.conn = DriverManager.getConnection(jdbc.getUrl(), jdbc.getUsername(), jdbc.getPassword());
			this.conn.setAutoCommit(autoCommit);
		} catch (ClassNotFoundException e) {
			log.error("openConnection error!", e);
			throw e;
		} catch (SQLException e) {
			log.error("openConnection error!", e);
			throw e;
		}
	}
	
	public <T> List<T> execute(String sql, ResultDealable<T> dealable) throws SQLException {
		PreparedStatement psm = null;
		ResultSet rs = null;
		try {
			psm = this.conn.prepareStatement(sql);
			rs = psm.executeQuery();
			return dealable.onResultSetCallback(rs);
		} catch(SQLException e) {
			log.error("PreparedStatement close error!");
			throw e;
		} finally {
			if(rs != null) {
				rs.close();
			}
			if(psm != null) {
				psm.close();
			}
		}
	}
	
	public ResultSet getPrimaryKey(String catalog, String schema, String table) throws SQLException {
		return this.conn.getMetaData().getPrimaryKeys(catalog, schema, table);
	}
	
	
	public void commit() throws SQLException {
		try {
			this.conn.commit();
		} catch (SQLException e) {
			log.error("connection close error!");
			throw e;
		}
	}
	
	public void close() throws SQLException {
		if(conn != null) {
			try {
				conn.close();
				conn = null;
			} catch (SQLException e) {
				log.error("connection close error!");
				throw e;
			}
		}
	}
	
}
